﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Xml;
using Catsland.Core;
using System.ComponentModel;

namespace Catsland.CatsPlugin
{
    public class QuadRender : CatComponent, Drawable
    {
        private VertexBuffer m_vertexBuffer;
        public VertexPositionColorTexture[] m_vertex;   // animator need to access

        private Vector2 m_size;
        private Vector2 m_offset;

        [CategoryAttribute("Location")]
        public Vector2 Size
        {
            get{ return m_size;}
            set
            {
                m_size = value;
                UpdateVertex();
            }
        }
        [CategoryAttribute("Location")]
        public Vector2 Offset
        {
            get { return m_offset; }
            set 
            { 
                m_offset = value; 
                UpdateVertex(); 
            }
        }

        public QuadRender(GameObject gameObject)
            : base(gameObject)
        {
        }

		public override CatComponent CloneComponent(GameObject gameObject)
		{
			QuadRender quadRender = new QuadRender(gameObject);
			quadRender.m_size = new Vector2(m_size.X, m_size.Y);
			quadRender.m_offset = new Vector2(m_offset.X, m_offset.Y);

			return quadRender;
		}

		public override void BindToScene(Scene scene)
		{
			base.BindToScene(scene);
			scene._renderList.AddItem(this);
		}

        /* Before initialize, m_size and m_offset should be set
         */
        public override void Initialize(Scene scene)
        {
            base.Initialize(scene);

            // check if model component exist
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                Console.WriteLine("Error! Null ModelComponent for QuadRender Component.");
                return;
            }

            // create mesh
            m_vertexBuffer = new VertexBuffer(Mgr<GraphicsDevice>.Singleton,
                typeof(VertexPositionColorTexture), 4, BufferUsage.None);
            m_vertex = new VertexPositionColorTexture[4];

            // TODO: texcoords should be set according to default animation
            Vector2[] texCoord = new Vector2[4];

			UpdateVertex();
            
            m_vertexBuffer.SetData<VertexPositionColorTexture>(m_vertex);

			if (scene != null)
			{
				scene._renderList.AddItem(this);
			}
		}

		public void UpdateVertex()
		{
			m_vertex[0] = new VertexPositionColorTexture(
				new Vector3(m_offset.X - m_size.X / 2.0f, m_offset.Y - m_size.Y / 2.0f, 0.0f),
				new Color(1.0f, 1.0f, 1.0f, 1.0f),
				new Vector2(0.0f, 1.0f));
			m_vertex[1] = new VertexPositionColorTexture(
				new Vector3(m_offset.X - m_size.X / 2.0f, m_offset.Y + m_size.Y / 2.0f, 0.0f),
				new Color(1.0f, 1.0f, 1.0f, 1.0f),
				new Vector2(0.0f, 0.0f));
			m_vertex[2] = new VertexPositionColorTexture(
				new Vector3(m_offset.X + m_size.X / 2.0f, m_offset.Y - m_size.Y / 2.0f, 0.0f),
				new Color(0.0f, 1.0f, 1.0f, 1.0f),
				new Vector2(1.0f, 1.0f));
			m_vertex[3] = new VertexPositionColorTexture(
				new Vector3(m_offset.X + m_size.X / 2.0f, m_offset.Y + m_size.Y / 2.0f, 0.0f),
				new Color(1.0f, 0.0f, 0.0f, 1.0f),
				new Vector2(1.0f, 0.0f));
		}

        public void Draw(int timeLastFrame)
        {
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                Console.Out.WriteLine("No ModelComponent for Animator.");
                return;
            }

			Mgr<GraphicsDevice>.Singleton.SetVertexBuffer(m_vertexBuffer);
			// configure effect
			// TODO: add height bias
			Vector3 position = new Vector3(m_gameObject.Position.X,
				m_gameObject.Position.Y * Mgr<Scene>.Singleton._yCos 
					+ m_gameObject.Height * Mgr<Scene>.Singleton._ySin,
				0.0f);
			modelComponent.Model._material.ConfigureMaterial(
				Mgr<Camera>.Singleton, position);

			Effect effect = modelComponent.Model._material._effect;
			foreach (EffectPass pass in effect.CurrentTechnique.Passes)
			{
				pass.Apply();
				Mgr<GraphicsDevice>.Singleton.DrawUserPrimitives<VertexPositionColorTexture>(
					PrimitiveType.TriangleStrip, m_vertex, 0, 2);
			}
        }

		public override bool SaveToNode(XmlNode node, XmlDocument doc)
		{
			XmlElement quadRender = doc.CreateElement(typeof(QuadRender).Name);
			node.AppendChild(quadRender);

			XmlElement size = doc.CreateElement("Size");
			quadRender.AppendChild(size);
			size.SetAttribute("Width", "" + m_size.X);
			size.SetAttribute("Height", "" + m_size.Y);

			XmlElement offset = doc.CreateElement("Offset");
			quadRender.AppendChild(offset);
			offset.SetAttribute("X", "" + m_offset.X);
			offset.SetAttribute("Y", "" + m_offset.Y);

			return true;
		}

        public override void ConfigureFromNode(XmlElement node, Scene scene, GameObject gameObject)
        {
            base.ConfigureFromNode(node, scene, gameObject);

            // size
            XmlElement size = (XmlElement)node.SelectSingleNode("Size");
            m_size = new Vector2(float.Parse(size.GetAttribute("Width")),
                                    float.Parse(size.GetAttribute("Height")));

            // offset
            XmlElement offset = (XmlElement)node.SelectSingleNode("Offset");
            m_offset = new Vector2(float.Parse(offset.GetAttribute("X")),
                                    float.Parse(offset.GetAttribute("Y")));
        }

		public override void Destroy()
		{
			Mgr<Scene>.Singleton._renderList.RemoveItem(this);
			base.Destroy();
		}


		public float GetDepth()
		{
			return m_gameObject.Position.Y;
		}

		public int CompareTo(object obj)
		{
			float otherDepth = ((Drawable)obj).GetDepth();
			float thisDepth = GetDepth();
			if (otherDepth > thisDepth)
			{
				return 1;
			}
			else if (otherDepth < thisDepth)
			{
				return -1;
			}
			return 0;
		}
	}
}
